%%%-------------------------------------------------------------------
%%% @author Rain
%%% @doc 帧同步行为回调定义
%%% @end
%%%-------------------------------------------------------------------
-module(elock_step_behaviour).
-author("Rain").

%% API
-export([call_client_match_fail/1]).
-export([call_client_match_succ/2]).
-export([call_client_room_not_find/3]).
-export([call_client_fight_start/4]).
-export([call_client_reconnect_succ/4]).
-export([call_client_check_hash_fail/3]).
-export([call_client_adjust_update_time/2]).
-export([call_client_one_frame_inputs/2]).
-export([time_ms/0]).
-export([set_elock_step_cb_mod/1]).
-export([log/3]).
-export([get_elock_step_update_interval/0]).
-export([set_elock_step_update_interval/1]).
-export([calc_frame_hash_and_snapshots/2]).
-export([call_client_match/2]).
-include("elock_step.hrl").

-callback call_client_match_fail(RoleId :: integer()) -> ok.
-callback call_client_match_succ(RoleId :: integer(), MatchId :: integer()) -> ok.
-callback call_client_room_not_find(RoleId :: integer(), MatchId :: integer(), Reason :: atom()) -> ok.
-callback call_client_fight_start(RoleId :: integer(), StartTime :: integer(), UnitInfos :: term(), RandomSeed :: integer()) -> ok.
-callback call_client_reconnect_succ(RoleId :: integer(), StartTime :: integer(), UnitInfos :: term(), Frame ::integer()) -> ok.
-callback call_client_check_hash_fail(RoleId :: integer(), Frame :: integer(), Units :: term()) -> ok.
-callback call_client_adjust_update_time(RoleId :: integer(), DiffTime :: integer()) -> ok.
-callback call_client_one_frame_inputs(RoleId :: integer(), OneFrameInputs :: term()) -> ok.
-callback log(LogType :: atom(), Format :: string(), Args :: [term()]) -> ok.
-callback time_ms() -> integer().
-callback calc_frame_hash_and_snapshots(Units :: term(), SnapShots :: binary()) -> {string(), binary()}.
-callback call_client_match(RoleId :: integer(), Result :: ok | {fail, Reason :: atom()}) -> ok.

%% @doc 获取回调模块
get_elock_step_cb_mod() ->
    persistent_term:get(elock_step_cb_mod).

%% @doc 设置回调模块
set_elock_step_cb_mod(Mod) ->
    persistent_term:put(elock_step_cb_mod, Mod).


%% @doc 获取回调模块
get_elock_step_update_interval() ->
    persistent_term:get(elock_step_update_interval).

%% @doc 设置回调模块
set_elock_step_update_interval(Interval) ->
    persistent_term:put(elock_step_update_interval, Interval).

%% @doc 通知客户端匹配失败
call_client_match_fail(RoleId) ->
    try
        Mod = get_elock_step_cb_mod(),
        Mod:call_client_fail(RoleId),
        ok
    catch
        Class:Error:Stack ->
            ?LS_ERROR("Class ~p Error ~p Stack ~p", [Class, Error, Stack]),
            throw(Class)
    end.

%% @doc 通知客户端匹配成功
call_client_match_succ(RoleId, MatchId) ->
    try
        Mod = get_elock_step_cb_mod(),
        Mod:call_client_match_succ(RoleId, MatchId),
        ok
    catch
        Class:Error:Stack ->
            ?LS_ERROR("Class ~p Error ~p Stack ~p", [Class, Error, Stack]),
            throw(Class)
    end.

%% @doc 通知客户端房间找不到，退出场景后重新匹配
call_client_room_not_find(RoleId, MatchId, Reason) ->
    try
        Mod = get_elock_step_cb_mod(),
        Mod:call_client_room_not_find(RoleId, MatchId, Reason),
        ok
    catch
        Class:Error:Stack ->
            ?LS_ERROR("Class ~p Error ~p Stack ~p", [Class, Error, Stack]),
            throw(Class)
    end.

%% @doc 毫秒时间接口
time_ms() ->
    try
        Mod = get_elock_step_cb_mod(),
        Mod:time_ms()
    catch
        Class:Error:Stack ->
            ?LS_ERROR("Class ~p Error ~p Stack ~p", [Class, Error, Stack]),
            throw(Class)
    end.

%% @doc 通知客户端战斗开始
call_client_fight_start(RoleId, StartTime, UnitInfos, RandomSeed) ->
    try
        Mod = get_elock_step_cb_mod(),
        Mod:call_client_fight_start(RoleId, StartTime, UnitInfos, RandomSeed),
        ok
    catch
        Class:Error:Stack ->
            ?LS_ERROR("Class ~p Error ~p Stack ~p", [Class, Error, Stack]),
            throw(Class)
    end.

%% @doc 通知客户端重连成功
call_client_reconnect_succ(RoleId, StartTime, UnitInfos, Frame) ->
    try
        Mod = get_elock_step_cb_mod(),
        Mod:call_client_reconnect_succ(RoleId, StartTime, UnitInfos, Frame),
        ok
    catch
        Class:Error:Stack ->
            ?LS_ERROR("Class ~p Error ~p Stack ~p", [Class, Error, Stack]),
            throw(Class)
    end.

%% @doc 通知客户端检查哈希失败
call_client_check_hash_fail(RoleId, Frame, Units) ->
    try
        Mod = get_elock_step_cb_mod(),
        Mod:call_client_check_hash_fail(RoleId, Frame, Units),
        ok
    catch
        Class:Error:Stack ->
            ?LS_ERROR("Class ~p Error ~p Stack ~p", [Class, Error, Stack]),
            throw(Class)
    end.

%% @doc 通知客户端调整更新时间,如果频率与客户端默认频率不同，客户端会动态调整为40ms和66ms的档位
call_client_adjust_update_time(RoleId, DiffTime) ->
    try
        Mod = get_elock_step_cb_mod(),
        Mod:call_client_adjust_update_time(RoleId, DiffTime),
        ok
    catch
        Class:Error:Stack ->
            ?LS_ERROR("Class ~p Error ~p Stack ~p", [Class, Error, Stack]),
            throw(Class)
    end.

%% @doc 通知客户端有一帧被输入了
call_client_one_frame_inputs(RoleId, OneFrameInputs) ->
    try
        Mod = get_elock_step_cb_mod(),
        Mod:call_client_one_frame_inputs(RoleId, OneFrameInputs),
        ok
    catch
        Class:Error:Stack ->
            ?LS_ERROR("Class ~p Error ~p Stack ~p", [Class, Error, Stack]),
            throw(Class)
    end.

%% @doc 计算帧号的二进制数据及其哈希值
%% 如果前后端需要验证一帧的数据前后端是否一致，这里建议是使用protobuf数据放入单位数据
%% 然后通过双端都执行相同的哈希算法，再比较，crypto:hash(sha256, PbData)
calc_frame_hash_and_snapshots(Units, SnapShots) ->
    try
        Mod = get_elock_step_cb_mod(),
        Mod:calc_frame_hash_and_snapshots(Units, SnapShots)
    catch
        Class:Error:Stack ->
            ?LS_ERROR("Class ~p Error ~p Stack ~p", [Class, Error, Stack]),
            throw(Class)
    end.

%% @doc 通知客户端匹配返回
call_client_match(RoleId, Result) ->
    try
        Mod = get_elock_step_cb_mod(),
        Mod:call_client_match(RoleId, Result)
    catch
        Class:Error:Stack ->
            ?LS_ERROR("Class ~p Error ~p Stack ~p", [Class, Error, Stack]),
            throw(Class)
    end.

%% @doc 日志
log(LogType, Format, Args) ->
    try
        Mod = get_elock_step_cb_mod(),
        Mod:log(LogType, Format, Args),
        ok
    catch
        Class:Error:Stack ->
            io:format("Class ~p Error ~p Stack ~p~n", [Class, Error, Stack]),
            io:format("[~p]: " ++ Format, [LogType | Args]),
            throw(Class)
    end.